package com.isesol.arch.web.servlets;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.filter.ThresholdFilter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 日志级别服务
 * 可以查看当前日志级别、修改某个path的日志级别
 *
 */
public class LoggerLevelServlet extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

		// 动作
		String action = StringUtils.defaultString(req.getParameter("action"), "loggers");

		String format = StringUtils.defaultString(req.getParameter("format"), "html");

		// 要修改的路径
		String path = StringUtils.defaultString(req.getParameter("path"));

		// 要修改的Level
		String level = req.getParameter("level");

		// appender
		String appender = req.getParameter("appender");

		// 过滤器——匹配时 ACCEPT、NEUTRAL、DENY
		String onMatch = req.getParameter("onmatch");

		// 过滤器——未匹配时 ACCEPT、NEUTRAL、DENY
		String onMismatch = req.getParameter("onmismatch");

		// 是否添加到日志文件
		boolean additive = BooleanUtils.toBoolean(StringUtils.defaultString(req.getParameter("additive"), "true"));

		String header="";
		String footer="";
		if (StringUtils.isNotBlank(format) && format.equals("json")) {

			resp.setContentType("application/json");

		}else if (StringUtils.isNotBlank(format) && format.equals("html")) {
			resp.setContentType("text/html");
			header="<html><body>";
			footer="</body></html>";
		}

		String respContent;
		switch ( action ) {
			case "change":
				respContent = changeLevel(path, level, additive, format);
				break;
			case "appender-add":
				respContent = appender(path, "add", appender, level, onMatch, onMismatch, additive, format);
				break;
			case "appender-remove":
				respContent = appender(path, "remove", appender, level, onMatch, onMismatch, additive, format);
				break;
			case "loggers":
				respContent = printLoggers(format);
				break;
			default:
				respContent = getLevel(path, format);
		}
		resp.getWriter().print(header + respContent +footer);
	}

	/**
	 * 添加Appender
	 *
	 * @param path
	 * @param appender
	 * @param level
	 * @param additive
	 * @return
	 */
	private String appender(String path, String action, String appender, String level,
	                        String onMatch, String onMismatch, boolean additive, String format) {

		LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
		Configuration config = ctx.getConfiguration();
		LoggerConfig loggerConfig = config.getLoggerConfig(path);
		Appender configAppender = config.getAppender(appender);
		Level levelObj = Level.getLevel(level);

		// 添加appender
		if (StringUtils.equals(action, "add")) {
			if (StringUtils.isNotBlank(onMatch) && StringUtils.isNotBlank(onMismatch)) {
				ThresholdFilter thresholdFilter = ThresholdFilter.createFilter(levelObj,
						Filter.Result.toResult(onMatch), Filter.Result.toResult(onMismatch));
				loggerConfig.addAppender(configAppender, levelObj, thresholdFilter);
				loggerConfig.addFilter(thresholdFilter);
			} else {
				loggerConfig.addAppender(configAppender, levelObj, null);
			}

			loggerConfig.setLevel(levelObj);
			loggerConfig.setAdditive(additive);
		} else if (StringUtils.equals(action, "remove")) {
			// 移除appender
			loggerConfig.removeAppender(appender);
		}
		ctx.updateLoggers();
		return printLogInfo(path, path, loggerConfig, format);
	}

	/**
	 * 打印所有的Logger配置
	 *
	 * @return
	 * @throws IOException
	 */
	private String printLoggers(String format) {

		LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
		Configuration config = ctx.getConfiguration();
		Map<String, LoggerConfig> loggers = config.getLoggers();

		StringBuilder sb = new StringBuilder();

		int count = 1;
		for(String path : loggers.keySet()){


			if (StringUtils.isNotBlank(format) && format.equals("json")) {

				sb.append(printLogInfo(path, "", loggers.get(path), format));
				sb.append(",");
			}else 	if (StringUtils.isNotBlank(format) && format.equals("html")) {

				sb.append("<hr/>");

				String caption = "index: <span style='color:red;font-weight:bold;'>" + count++ + "</span>, path: <span style='color:red;" +
						"font-weight:bold;'>" + StringUtils.defaultIfBlank(path, "ROOT") + "</span>";
				sb.append(printLogInfo(path, caption, loggers.get(path), format));
			}

		}
		return sb.toString();
	}

	private String getLevel(String path, String format) {

		LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
		Configuration config = ctx.getConfiguration();
		LoggerConfig loggerConfig = config.getLoggerConfig(path);

		return printLogInfo(path, path, loggerConfig, format);
	}

	/**
	 * 打印单个logger的配置
	 *
	 * @param path
	 * @param caption
	 * @param loggerConfig
	 * @return
	 * @throws IOException
	 */
	private String printLogInfo(String path, String caption, LoggerConfig loggerConfig, String format) {

		Map<String, Object> params = new HashMap<>();
		params.put("Path:", path);
		params.put("Level: ", loggerConfig.getLevel());
		params.put("Parent: ", loggerConfig.getParent());
		params.put("Appenders: ", loggerConfig.getAppenders());
		params.put("AppenderRefs: ", loggerConfig.getAppenderRefs());
		params.put("Additive: ", loggerConfig.isAdditive());
		params.put("Filter: ", loggerConfig.getFilter());
		params.put("Properties: ", loggerConfig.getProperties());

		StringBuilder sb = new StringBuilder();

		if (StringUtils.isNotBlank(format) && format.equals("json")) {

			Map logInfoMap= Maps.newHashMap();
			for(String key : params.keySet()){
				if(null==params.get(key))				{
					logInfoMap.put(key, null);
				}else{
					logInfoMap.put(key, params.get(key).toString());
				}

			}
			sb.append(JSON.toJSONString(logInfoMap,true));
		}

		if (StringUtils.isNotBlank(format) && format.equals("html")) {

			String baseStyle = "border-bottom: 1px solid #ddd; border-right: 1px solid #ddd; padding: 0.5em";
			sb.append("<table>");
			sb.append("<caption>" + caption + "</caption>");
			sb.append("<thead>");
			sb.append("<tr>");
			sb.append("<th style='" + baseStyle + ";border-top: 1px solid #ddd'>Key</th>");
			sb.append("<th style='" + baseStyle + ";border-top: 1px solid #ddd'>Value</th>");
			sb.append("</thead>");
			sb.append("<tbody>");

			for(String key : params.keySet()){
				sb.append("<tr><td style='color:red;font-weight:bold;" + baseStyle + "'>" + key + "</td><td style='" + baseStyle + "'>" +
                        params.get(key.toString()) + "</td></tr>");
			}
			sb.append("</tbody>");
			sb.append("</table>");
		}

		return sb.toString();
	}

	/**
	 * 更改日志级别
	 *
	 * @param path
	 * @param level
	 * @throws IOException
	 */
	private String changeLevel(String path, String level, boolean additive, String format) {

		LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
		Configuration config = ctx.getConfiguration();
		LoggerConfig loggerConfig = config.getLoggerConfig(path);
		loggerConfig.setLevel(Level.getLevel(level));
		loggerConfig.setAdditive(additive);
		ctx.updateLoggers();

		return printLogInfo(path, path, loggerConfig, format);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {

	}

}